//响应式原理:组件中data更新后,视图会立即刷新
//响应式使用的是:发布订阅模式,即:数据劫持,收集依赖
// 数据劫持原理:使用Object.defineproerty方法,改写data的get和set属性,在getter和setter中完成数据劫持
//收集依赖原理:
//构造一个Dep类,在Dep中有收集依赖和通知更新的方法,在每个数据劫持的时候,构造一个Dep实例,在getter和setter中使用收集依赖和通知更新的方法
//构造一个Watcher类,用来模仿模板解析中对数据的使用,watcher中有访问数据和更新数据的方法,当每次访问数据时,实例化一个Watcher,,第一次访问数据时会被Dep保存存起来,当数据更新时,Dep通知watcher调用更新方法,重新获取数据,视图更新
let W = null
class Watcher {
    constructor(obj, key) {
        this.obj = obj
        this.key = key
        W = this
        this.get()
        W = null
    }
    get() {
        return this.obj[this.key]
    }
    update() {
        console.log('通知更新');
        get()
    }
}

class Dep {
    constructor() {
        this.subs = []
    }
    collect(watcher) {
        this.subs.push(watcher)
    }
    notify() {
        
        this.subs.forEach(item => {
            item.update()
        })
    }
}


class Observe {
    constructor(obj) {
        this.obj = obj
        if (Array.isArray(obj)) {
            this.arrayGo()
        } else {
            this.walk(obj)
        }
    }
    arrayGo() {
        this.obj.forEach(element => {
            observe(element)
        });
    }
    walk(obj) {
        for (const key of Object.keys(obj)) {
            defineReactive(obj, key, obj[key])
        }
    }
}


function defineReactive(obj, key, value) {
    observe(value)

    const dep = new Dep()
    Object.defineProperty(obj, key, {
        get() {
            
            if(W){
                dep.collect(W)
            }
            return value
        },
        set(newVal) {
            value = newVal

            dep.notify()
        }
    })
}



function observe(obj) {

    if (typeof obj !== 'object' || obj === null) return

    new Observe(obj)
}


function Vue(options) {
    this._data = options.data

    for (const key of Object.keys(this._data)) {
        Object.defineProperty(this, key, {
            get() {
                return this._data[key]
            },
            set(newVal) {
                this._data[key] = newVal
            }
        })
    }
    observe(this._data)
}

const vm = new Vue({
    data: {
        name: 'wnag',
        age: 19,
        hobby: [{
                id: '001',
                type: '工作'
            },
            {
                id: '002',
                type: '旅游'
            },
            {
                id: '003',
                type: '社交'
            },
        ]
    }
})

function get() {
    // console.log(new Watcher(vm,'name'));
    console.log(new Watcher(vm.hobby[0],'type'));
}

function set() {
    // vm.name = 'zhang'
    vm.hobby[0].type='睡觉'
}

get()
set()